﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.ServiceProcess;

namespace EventLogMailer
{
    public class Program : ServiceApplicationBase
    {
        private MailerConfiguration configuration;

        private ApplicationState applicationState;

        public static TraceSource TraceSource = new TraceSource("Application");

        public Program()
            : base(TraceSource)
        {
        }

        static void Main(string[] args)
        {
            new Program().Run(args);
        }

        private void ProcessEventLog(EventLog eventLog, DateTime lastSentMail)
        {
            var recipientEntries = new Dictionary<string, List<EventLogEntry>>();

            var recentLogEntries = eventLog.Entries.Cast<EventLogEntry>().Where(e => e.TimeWritten > lastSentMail);
            foreach (EventLogEntry logEntry in recentLogEntries)
            {
                foreach (var mailRule in this.configuration.Rules)
                {
                    if (mailRule.Matches(logEntry))
                    {
                        if(recipientEntries.ContainsKey(mailRule.Recipient)==false)
                        {
                            recipientEntries[mailRule.Recipient] = new List<EventLogEntry>();
                        }

                        recipientEntries[mailRule.Recipient].Add(logEntry);
                    }
                }
            }

            foreach (var recipientEntry in recipientEntries)
            {
                Mailer.SendMail(new[]{recipientEntry.Key}, recipientEntry.Value);
            }

            this.applicationState.LastSentMail = DateTime.Now;
            ApplicationState.Save(this.applicationState);
        }

        private static MailerConfiguration LoadConfiguration(string filePath)
        {
            filePath = PathUtils.ResolveApplicationExecutableRelative(filePath);
            var content = File.ReadAllText(filePath);
            var configuration = Xml.Deserialize<MailerConfiguration>(content);

            foreach (var mailRule in configuration.Rules)
            {
                mailRule.Validate();
            }

            return configuration;
        }

        private static void SaveSampleConfig(string filePath, bool overwrite = false)
        {
            filePath = PathUtils.ResolveApplicationExecutableRelative(filePath);
            if (File.Exists(filePath) && overwrite == false)
            {
                return;
            }

            var content = Xml.Serialize(MailerConfiguration.CreateDefault());
            File.WriteAllText(filePath, content);
        }

        private void OnEntryWritten(object sender, EntryWrittenEventArgs e)
        {
            var applicationLastSentMail = this.applicationState.LastSentMail;
            var lastSentMailInterval = (DateTime.Now - applicationLastSentMail).TotalSeconds;

            if (lastSentMailInterval < this.configuration.SendingPause)
            {
                return;
            }

            var entry = e.Entry;
            var recipients = this.configuration.Rules.Where(mailRule => mailRule.Matches(entry)).Select(mailRule => mailRule.Recipient).ToList();

            if (recipients.Any())
            {
                Mailer.SendMail(recipients, new[] { entry });
                this.applicationState.LastSentMail = DateTime.Now;
                ApplicationState.Save(this.applicationState);
            }
        }

        protected override void StopServer()
        {
            
        }

        protected override void StartServer(string[] args)
        {
            SaveSampleConfig("mailer.config");
            SaveSampleConfig("mailer.sample.config", true);

            this.configuration = LoadConfiguration("mailer.config");
            this.applicationState = ApplicationState.Load();

            var lastSentMail = applicationState.LastSentMail < DateTime.Now.AddDays(-1) ? DateTime.Now.AddDays(-1) : applicationState.LastSentMail;

            foreach (var monitoredEventLog in configuration.MonitoredEventLogs)
            {
                var eventLog = new EventLog(monitoredEventLog)
                {
                    EnableRaisingEvents = true
                };
                eventLog.EntryWritten += OnEntryWritten;

                this.ProcessEventLog(eventLog, lastSentMail);
            }

            Program.TraceSource.TraceEvent(TraceEventType.Verbose, 0, "Waiting");
        }

        protected override ServiceInstaller CreateServiceInstaller()
        {
            return new ServiceInstaller
            {
                ServiceName = "EventLogMailer",
                DisplayName = "EventLogMailer",
                StartType = ServiceStartMode.Automatic,
                Description =
                    "Sends email when event matching rule is writtn to system event log"
            };
        }
    }
}
